#import "TransferView.h"
#import "TransferViewElement.h"
#import "FOutlineView.h"
#import "Transfers.h"
#import "SolarSeekApplication.h"
#import "SourceListElement.h"
#import "TransfersController.h"
#import "ApplicationDefaults.h"

#import "TransfersController.h"
#import "SolarSeekEngine.h"
#import "FProgressCell.h"

#import "ChatsController.h"
#import "Chats.h"
#import "ChatElements.h"
#import "FriendsSourceListElement.h"
#import "Shares.h"

typedef enum { TVCreate=1,TVRemove=2, TUpdate=3 } TransferViewObservingTypes;


@implementation TransferView

- (void)awakeFromNib {
	[nibOutlineView setDelegate:self];
	[nibOutlineView setDoubleAction:@selector(revealTransfers:)];
	
	[NSApp addObserver:self forKeyPath:@"engine.transfersController.transferCreated" options:nil context:(void*)TVCreate];
	[NSApp addObserver:self forKeyPath:@"engine.transfersController.transferRemoved" options:nil context:(void*)TVRemove];
	
	[self performSelector:@selector(autoloadDownloadInfos) withObject:nil afterDelay:0.5];
}


- (void)dealloc {
	[[[nibOutlineView tableColumnWithIdentifier:@"percentageComplete"] dataCell] release];
	[NSApp removeObserver:self forKeyPath:@"engine.transfersController.transferCreated"];
	[NSApp removeObserver:self forKeyPath:@"engine.transfersController.transferRemoved"];
	[super dealloc];
}

- (TransferViewElement *)transferViewElementForTransfer:(Transfer *)transfer {
	NSEnumerator *e = [[(FOutlineViewArrayElement *)[nibOutlineView root] children] objectEnumerator];
	TransferViewElement *tve;
	while(tve = [e nextObject]) {
		if([transfer isEqual:[tve data]]) 
			break;
	}
	return tve;
}

- (NSArray *)participantsSelected {
	NSIndexSet *sels = [nibOutlineView selectedRowIndexes];
	unsigned c = [sels count];
	unsigned int index = [sels firstIndex];
	Chat *chat = nil;
	NSMutableSet *peers = [NSMutableSet set];
	while(c--) {
		Transfer *t = (Transfer *)[[nibOutlineView itemAtRow:index] data];
		[peers addObject:[t peer]];
	}
	return [peers allObjects];
}

- (NSArray *)sharesSelected {
	NSIndexSet *sels = [nibOutlineView selectedRowIndexes];
	unsigned c = [sels count];
	unsigned int index = [sels firstIndex];
	Chat *chat = nil;
	NSMutableArray *shares = [NSMutableArray array];
	while(c--) {
		Transfer *t = (Transfer *)[[nibOutlineView itemAtRow:index] data];
		[shares addObject:[t share]];
	}
	return shares;
}

- (void)copyString:(NSString *)string {
	NSPasteboard *pb = [NSPasteboard generalPasteboard];
	[pb addTypes:[NSArray arrayWithObjects:NSStringPboardType,nil] owner:self];
	[pb setString:string forType:NSStringPboardType];
}

- (IBAction)copyFileNames:(id)sender {
	NSIndexSet *sels = [nibOutlineView selectedRowIndexes];
	unsigned c = [sels count];
	unsigned int index = [sels firstIndex];
	NSMutableArray *a = [NSMutableArray arrayWithCapacity:c];
	int inProgress = 0;
	while(c--) {
		Transfer *tt = (Transfer *)[[nibOutlineView itemAtRow:index] data];
		if([tt state]==TransferStateTransferring && [tt type]==TransferTypeDownload)
				inProgress++;
		[a addObject:tt];
		index = [sels indexGreaterThanIndex:index];
	}
	NSMutableString *string = [[NSMutableString alloc] init];
	Transfer *t;
	BOOL first = YES;
	NSEnumerator *e = [a objectEnumerator];
	while(t = [e nextObject]) {
		if(first)
			first = NO;
		else
			[string appendString:@", "];
		[string appendString:[t filenameCurrent]];
	}
	[self copyString:string];
	[string autorelease];
}

	


- (TransferType)type { return type; }
- (void)setTransferType:(TransferType)t { type = t; }

- (void)animateGo {
	[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(animateGo) object:nil];
	BOOL more = NO;
	NSEnumerator *e = [[(FOutlineViewArrayElement *)[nibOutlineView root] children] objectEnumerator];
	TransferViewElement *tve;
	while(tve = [e nextObject]) {
		TransferState state = [(Transfer *)[tve data] state];
		switch(state) {
			case TransferStateTransferring:
			case TransferStateStarting:
				more = YES;
				[nibOutlineView reloadItem:tve];
				break;
		}
	}
	
	[NSApp updateSourceListElement:[NSApp sourceListElementForView:self]];
	
	if(more)
		[self performSelector:@selector(animate) withObject:nil afterDelay:0.33];
}
			

- (void)animate {
	[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(animate) object:nil];
	[self performSelector:@selector(animateGo) withObject:nil afterDelay:0.0];
}

- (NSString *)autosavePathForDownload:(TransferDownload *)download {
	NSString *sharename = [[download share] filenameSharedUnix];
	NSString *path = [[NSString stringWithFormat:@"~/Library/Application Support/SolarSeek/%@.%@.%d.download.plist",
			[[sharename lastPathComponent] stringByDeletingPathExtension],
			[[download peer] name],
			[sharename hash]
			] stringByExpandingTildeInPath];
	return path;
}

- (void)deleteDownloadInfo:(TransferDownload *)download {
	NSString *path = [self autosavePathForDownload:download];
	[[NSFileManager defaultManager] removeFileAtPath:path handler:nil];
//		NSLog(@"ERROR: Unable to automatically delete download at %@",path);
}

- (void)saveDownloadInfo:(TransferDownload *)download {
	NSString *path = [self autosavePathForDownload:download];
	NSString *dir = [path stringByDeletingLastPathComponent];
	if([[NSFileManager defaultManager] fileExistsAtPath:dir] == YES || 
			[[NSFileManager defaultManager] createDirectoryAtPath:dir attributes:nil]==YES) {
		if([[[NSApp engine] transfersController] saveDownload:download toFile:path])
			return;

	}
	[[NSAlert alertWithMessageText:@"Unable to save information about transfer" defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"SolarSeek was unable to automatically save information about the transfer to %@.  This will NOT prevent SolarSeek from downloading the file.  However, if you close the application, this download will not automatically resume.",path] beginSheetModalForWindow:[self window] modalDelegate:nil didEndSelector:nil contextInfo:nil];
}

- (void)loadDownloadInfoFromPath:(NSString *)path {
	[[[NSApp engine] transfersController] loadDownloadFromFile:[path stringByExpandingTildeInPath]];
}


- (void)autoloadDownloadInfos {
	[NSThread detachNewThreadSelector:@selector(autoloadDownloadInfosInBackground) toTarget:self withObject:nil];
}

- (void)autoloadDownloadInfosInBackground {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	NS_DURING
		if([self type] == TransferTypeDownload) {
			[NSApp appendLogWithHeader:@"AUTOLOAD" HTML:@"Loading downloads" type:LogTypeTransfer];
			NSFileManager *fm = [NSFileManager defaultManager];
			NSString *dir = [@"~/Library/Application Support/SolarSeek/" stringByExpandingTildeInPath];
			if([fm fileExistsAtPath:dir]) {
				NSDirectoryEnumerator *dirEnum = [fm enumeratorAtPath:dir];
				NSString *file;
				while(file = [dirEnum nextObject]) {
					if([[file pathExtension] isEqualToString:@"plist"] && 
							[[[file stringByDeletingPathExtension] pathExtension] isEqualToString:@"download"]) {
						[NSApp appendLogWithHeader:@"DOWNLOAD" HTML:[NSString stringWithFormat:@"Loading download from %@",[file stringByDeletingPathExtension]] type:LogTypeTransfer|LogTypeLoud];
						file = [dir stringByAppendingPathComponent:file];
						[self loadDownloadInfoFromPath:file];
					}
				}
			}
		}
	NS_HANDLER
		NSLog([localException description]);
	NS_ENDHANDLER
	[pool release];
}
	

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
	switch((int)context) {
		case TVCreate: {
			Transfer *t = [[[NSApp engine] transfersController] transferCreated];
			if(t && [t type]==type) {
				[t addObserver:self forKeyPath:@"descriptionState" options:nil context:(void*)TUpdate];
				TransferViewElement *tve = [[TransferViewElement alloc] initWithTransfer:t];
				[(FOutlineViewArrayElement *)[nibOutlineView root] addElement:tve];
				if([t state]==TransferStateStarting)
					[self animate];
				[nibOutlineView reloadData];
				if([self type]==TransferTypeDownload) {
						[self saveDownloadInfo:(TransferDownload *)t];
				}
			}
			break;
		}
		case TVRemove: {
			Transfer *t = [[[NSApp engine] transfersController] transferRemoved];
			if(t && [t type]==type) {
				[t removeObserver:self forKeyPath:@"descriptionState"];
				TransferViewElement *tve = [self transferViewElementForTransfer:t];
				if([nibOutlineView isRowSelected:[nibOutlineView rowForItem:tve]]) {
					[nibOutlineView deselectAll:self];
//					[nibOutlineView deselectRow:[nibOutlineView rowForItem:tve]];
				}
				if(tve) {
					[(FOutlineViewArrayElement *)[nibOutlineView root] removeElement:tve];
					[tve release];
				}
				[nibOutlineView reloadData];
				if([t type] == TransferTypeDownload) {
					[self deleteDownloadInfo:(TransferDownload *)t];
				}
			}
			break;
		}
		case TUpdate: {
			if(object && [(Transfer *)object type] == type) {
				TransferState state = [(Transfer *)object state];
				//update on first start -> non starting (transferring or fail)
				//transferring -> not transferring;
				//first start -> queued
				//queued to not queued or first start
				//failed -> transferring.
				TransferViewElement *tve = [self transferViewElementForTransfer:object];

				BOOL attention = NO;
				switch(state) {
				case TransferStateTransferring:
					[self animate];
					//continue on down. no break
				case TransferStateFinished:
					if(state == TransferStateFinished && type == TransferTypeDownload)
						[self deleteDownloadInfo:object];
					//continue on down.  no break.
				case TransferStateFailed:
				case TransferStateIdle:
					attention = [tve state]!=state;
					[tve setState:state];
					break;
				}
				
				[nibOutlineView reloadItem:tve];
				[[NSApp sourceListElementForView:self] refreshAndRequestAttention:attention];
			}
			break;
		}
		default:
			[NSException raise:@"DumbException" format:@"We shouldn't be observing this object here."];
	}
}

- (void)alertDidEnd:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void *)contextInfo {
	NSArray *a = contextInfo;
	switch(returnCode) {
		case NSAlertDefaultReturn: {
			NSEnumerator *e = [a objectEnumerator];
			Transfer *t;
			while(t = [e nextObject]) {
				if([t state] != TransferStateTransferring) {
					[[[NSApp engine] transfersController] deleteTransfer:t];
				}
			}
			break;
		}
		case NSAlertOtherReturn: {
			NSEnumerator *e = [a objectEnumerator];
			Transfer *t;
			while(t = [e nextObject]) {
				[[[NSApp engine] transfersController] stopTransfer:t];
				[[[NSApp engine] transfersController] deleteTransfer:t];
			}
			break;
		}
	}
	[a release];
}						

- (IBAction)clearTransfers:(id)sender {
	NSIndexSet *sels = [nibOutlineView selectedRowIndexes];
	unsigned c = [sels count];
	unsigned int index = [sels firstIndex];
	NSMutableArray *a = [NSMutableArray arrayWithCapacity:c];
	int inProgress = 0;
	while(c--) {
		Transfer *tt = (Transfer *)[[nibOutlineView itemAtRow:index] data];
		if([tt state]==TransferStateTransferring && [tt type]==TransferTypeDownload)
				inProgress++;
		[a addObject:tt];
		index = [sels indexGreaterThanIndex:index];
	}
	[nibOutlineView deselectAll:sender];
	if(type==TransferTypeDownload && inProgress > 0 && [[NSUserDefaults standardUserDefaults] boolForKey:SolarSeekDefaultPromptBeforeRemovingPartiallyDownloadedFiles]) {
		NSAlert *alert = [NSAlert alertWithMessageText:@"Downloads in Progress" 
								defaultButton:@"Keep Downloads in Progress" 
								alternateButton:@"Cancel"
								otherButton:@"Delete All"
								informativeTextWithFormat:@"There are %d downloads (out of %d) still in progress.  Would you like to keep downloading these files and only remove the ones that are finished or stopped?",inProgress,[a count]];
		[alert setAlertStyle:NSInformationalAlertStyle];
		[alert beginSheetModalForWindow:[NSApp keyWindow] modalDelegate:self didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:[a retain]];
	}
	else {
		NSEnumerator *e = [a objectEnumerator];
		Transfer *t;
		while(t = [e nextObject])
			[[[NSApp engine] transfersController] deleteTransfer:t];
	}
}

- (IBAction)select:(id)sender {
	[NSApp selectSourceListElement:[NSApp sourceListElementForView:self]];
}

- (IBAction)removeFinishedTransfers:(id)sender
{
	[nibOutlineView deselectAll:sender];
	NSEnumerator *e = [[[[NSApp engine] transfersController] transfersWithType:[self type]] objectEnumerator];
	Transfer *t;
	NSMutableArray *a = [NSMutableArray array];
	while(t = [e nextObject]) {
		if([t state] == TransferStateFinished)
			[a addObject:t];
	}
	e = [a objectEnumerator];
	while(t = [e nextObject]) {
		[[[NSApp engine] transfersController] deleteTransfer:t];
	}
}

- (IBAction)restartTransfers:(id)sender {
	NSIndexSet *sels = [nibOutlineView selectedRowIndexes];
	unsigned c = [sels count];
	unsigned int index = [sels firstIndex];
	NSMutableArray *a = [NSMutableArray arrayWithCapacity:c];
	while(c--) {
		[a addObject:[[nibOutlineView itemAtRow:index] data]];
		index = [sels indexGreaterThanIndex:index];
	}
	NSEnumerator *e = [a objectEnumerator];
	Transfer *td;
	while(td = [e nextObject]) {
		if([td state] != TransferStateTransferring)
			[[[NSApp engine] transfersController] downloadShare:(SharePeer *)[td share]];
	}
}

- (IBAction)openTransfers:(id)sender {
	NSIndexSet *sels = [nibOutlineView selectedRowIndexes];
	unsigned c = [sels count];
	unsigned int index = [sels firstIndex];
	NSMutableArray *a = [NSMutableArray arrayWithCapacity:c];
	while(c--) {
		Transfer *t = (Transfer *)[[nibOutlineView itemAtRow:index] data];
		[[NSWorkspace sharedWorkspace] openFile:[[t filenameCurrent] stringByExpandingTildeInPath]];
	}
}

- (IBAction)revealTransfers:(id)sender {
	NSIndexSet *sels = [nibOutlineView selectedRowIndexes];
	unsigned c = [sels count];
	unsigned int index = [sels firstIndex];
	NSMutableArray *a = [NSMutableArray arrayWithCapacity:c];
	while(c--) {
		Transfer *t = (Transfer *)[[nibOutlineView itemAtRow:index] data];
		[a addObject:[t filenameCurrent]];
	}
	[NSApp revealStrings:a];
}	

- (IBAction)stopTransfers:(id)sender {
	NSIndexSet *sels = [nibOutlineView selectedRowIndexes];
	unsigned c = [sels count];
	unsigned int index = [sels firstIndex];
	NSMutableArray *a = [NSMutableArray arrayWithCapacity:c];
	while(c--) {
		[a addObject:[[nibOutlineView itemAtRow:index] data]];
		index = [sels indexGreaterThanIndex:index];
	}
	NSEnumerator *e = [a objectEnumerator];
	Transfer *t;
	while(t = [e nextObject])
		[[[NSApp engine] transfersController] stopTransfer:t];
}

@end
